using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Oops.Xna.Framework.Viewpoint;

namespace Oops.Xna.Framework.Input
{
	// TODO : Allow for multiple cursors.
	// TODO : Allow for game-screen-specific functionality.
	public class CursorComponent : DrawableGameComponent
	{
		private IInputService inputService;
		private IViewpointService viewpointService;

		private int controllerIndex;
		
		private Vector2 position;
		private float speed = 200.0f;
		private string textureName;

		private SpriteBatch spriteBatch;
		private Texture2D texture;
		private Vector2 origin;

		public CursorComponent(Game game) : base(game)
		{
			// The game parameter must be specified.
			if (game == null)
			{
				throw new ArgumentNullException("game");
			}
		}

		public Vector2 Position
		{
			get { return this.position; }
		}

		public float Speed
		{
			get { return this.speed; }
			set { this.speed = value; }
		}

		public string TextureName
		{
			get { return this.textureName; }
			set
			{
				if (this.textureName != value)
				{
					this.textureName = value;

					this.texture = this.Game.GetContent().Load<Texture2D>(this.textureName);
					this.origin = new Vector2(this.texture.Width >> 1, this.texture.Height >> 1);
				}
			}
		}

		#region IGameComponent Members 

		public override void Initialize()
		{
			// Get the required game services.
			this.inputService = this.Game.GetService<IInputService>();
			this.viewpointService = this.Game.GetService<IViewpointService>();

			// Call inherited method.
			base.Initialize();
#if XBOX
			// Get the current viewport.
			Viewport viewport = this.GraphicsDevice.Viewport;

			// Center the position.
            this.position.X = viewport.X + (viewport.Width >> 1);
            this.position.Y = viewport.Y + (viewport.Height >> 1);
#endif
		}

		#endregion

		#region IUpdateable Members

		public override void Update(GameTime gameTime)
		{
			// Get the elapsed time.
			float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

			// we'll create a vector2, called delta, which will store how much the
			// cursor position should change.
			Vector2 delta = this.inputService.GetGamePadLeftThumbStick(this.controllerIndex);
			delta.Y *= -1.0f;

			// check the dpad: if any of its buttons are pressed, that will change delta
			// as well.
			if (this.inputService.IsGamePadButtonDown(this.controllerIndex, Buttons.DPadUp) || this.inputService.IsKeyDown(Keys.W))
			{
				delta.Y = -1.0f;
			}
			if (this.inputService.IsGamePadButtonDown(this.controllerIndex, Buttons.DPadDown) || this.inputService.IsKeyDown(Keys.S))
			{
				delta.Y = 1.0f;
			}
			if (this.inputService.IsGamePadButtonDown(this.controllerIndex, Buttons.DPadLeft) || this.inputService.IsKeyDown(Keys.A))
			{
				delta.X = -1.0f;
			}
			if (this.inputService.IsGamePadButtonDown(this.controllerIndex, Buttons.DPadRight) || this.inputService.IsKeyDown(Keys.D))
			{
				delta.X = 1.0f;
			}

			// normalize delta so that we know the cursor can't move faster than
			// CursorSpeed.
			if (delta.X != 0.0f || delta.Y != 0.0f)
			{
				delta.Normalize();
			}
#if XBOX
            // modify position using delta, the CursorSpeed constant defined above, and
            // the elapsed game time.
            this.position += delta * this.speed * elapsed;

            // clamp the cursor position to the viewport, so that it can't move off the
            // screen.
            Viewport viewport = this.GraphicsDevice.Viewport;
            this.position.X = MathHelper.Clamp(position.X, viewport.X, viewport.X + viewport.Width);
            this.position.Y = MathHelper.Clamp(position.Y, viewport.Y, viewport.Y + viewport.Height);
#else
			this.position.X = this.inputService.MouseX;
			this.position.Y = this.inputService.MouseY;
			
			// modify position using delta, the CursorSpeed constant defined above, and
			// the elapsed game time.
			this.position += delta * this.speed * elapsed;

			// set the new mouse position using the combination of mouse and gamepad
			// data.
			this.inputService.SetMousePosition((int)this.position.X, (int)this.position.Y);
#endif
			// Call inherited method.
			base.Update(gameTime);
		}

		#endregion

		#region IDrawable Members

		protected override void LoadContent()
		{
			// Create the sprite batch.
			this.spriteBatch = new SpriteBatch(this.GraphicsDevice);

			// Call inherited method.
			base.LoadContent();
		}

		protected override void UnloadContent()
		{
			// Dispose of the sprite batch.
			if (this.spriteBatch != null)
			{
				this.spriteBatch.Dispose();
			}

			// Call inherited method.
			base.UnloadContent();
		}

		public override void Draw(GameTime gameTime)
		{
			// Draw the cursor.
			this.spriteBatch.Begin(
				SpriteBlendMode.AlphaBlend, 
				SpriteSortMode.Immediate, 
				SaveStateMode.SaveState);
			this.spriteBatch.Draw(
				this.texture, 
				this.position, 
				null, 
				Color.White, 
				0.0f,
				this.origin, 
				1.0f, 
				SpriteEffects.None, 
				0.0f);
			this.spriteBatch.End();

			// Call inherited method.
			base.Draw(gameTime);
		}

		#endregion
	}
}